Welcome back.
In this lecture we're going to take a look at some built in types.
Roblox Liu has three built in types that exist for the type engine, and these types are any unknown
and never.
Now you've seen me talk about any earlier in the course, and in this lecture, we're going to actually
look at the true form of any.
I'm going to create a variable here.
I'm going to call it my var.
And I'm not going to give it a value, nor am I going to give it a type annotation.
Now, when I type out the name of this variable, you'll see that this letter here says a.
Now, I told you that this was any in a previous lecture, but in actuality, that's not the true form
of any.
While we can set this variable to be whatever we'd like, such as we could set it to be a string.
Once we give it this value, it's going to update the type of this variable to be of the value that
we set it to be, which was a string.
And now this variable is going to forever be of the type string.
So if I try to set it to be another type such as a boolean, we're going to get a red underline here.
And that's because we can't convert this type boolean into the type string.
So we originally initialized it to have no value.
And then we set the value to the string.
And it gave it the type of string.
And now this variable is going to forever be of the type string.
However, if I want to be able to override this variable to whatever I'd like, then this is where the
real any type comes into play.
The any type is a special kind of type that allows itself to be used as an arbitrary type that can stand
for anything.
This type allows us to skip the process of type refinement that is needed for the type engine to figure
out what exactly the type of a variable is.
And of course, we're going to take a look at type refinement in a little bit.
So basically when we create a new variable and we don't assign it any value, it can inherit any value
and doesn't have a type.
But once it's assigned a value like we did right here, the type of that value is set for the variable.
If we want to continually override the variable, then we have to explicitly set the type of this variable
to be of the type any.
And now we have that red underline disappear.
And that's because my var is going to forever be of the type any.
It's no longer a string or a boolean, but instead it is of the type.
Any the any type is just basically an opt out of the type system entirely.
So if we don't want to have to bother with a variable being underlined when we override its value while
we're in the strict type inferencing mode, then we can just set it to any and then it opts out this
variable to be of the type checking system, so we can set it to whatever we'd like, and we don't get
any red underlines.
Our next type is the unknown type, and this one is going to be a little more interesting as it's considered
to be a top type.
Now, a top type means that this is a union of every other type in our game, and for us to be able
to figure out what the exact type of a variable is, we have to use type refinement.
Or basically we got to use if statements and some functions to let studio know exactly what the type
of a variable is.
So I'm going to create a function here and it's going to be called get unknown.
And inside of this function we're going to do a little bit of some syntax magic.
And I'm going to return.
And then I'm going to put an if statement here.
So if math dot random is greater than 0.5 then we're going to return a string of some string.
Otherwise five.
And what this does is that we're returning some string in case math dot random is greater than 0.5.
Otherwise we're returning five.
And as you can see, we don't have to put an end statement here because we're using a little bit of
some syntax magic.
Now we can denote this function as returning the type unknown.
Because we're not going to have any idea what this function is going to return.
It could return a string or it could return a number.
We don't know.
And now that means, because we've set the return of this function to be of the unknown type, when
we get the value that gets returned from this function, a is going to be of the type unknown.
And that means we cannot set the type annotation for this variable.
So if I try to set it to a string, we're going to get a problem here okay.
Because we can't convert the type unknown into string.
Same thing goes for if we had another variable and we try to set it to be a number equal to the get
unknown function, again, we're going to get an error and it's even not going to work.
If we had a variable with a union type and we set it to either a string or a number.
So if we do this.
We're still going to get a red underline.
How do we solve this conundrum?
Well, that's where type refinement comes into play.
We have to refine the type that gets returned from this function.
For us to be able to figure out what exactly it is.
And we use that by using three different functions.
One function is assert, one function is type, and the other function is type of.
Now you might have heard of these functions if you were in my previous beginner's lecture, but if you
haven't then I'm going to explain them right now for you.
All right.
So first off the assert function is a function that we can use to manually throw errors.
If a given argument to the function is falsy, we can give it any value.
And it's going to check if the value is false or nil, and if so it can throw a custom error message
we give it.
It's just like the error function, but it checks if a value is falsy for us if we need that kind of
functionality.
So I'm going to create a variable here called some bool.
And I'm going to set it to false.
And then I can call the assert function and pass some bool to this function.
And then I can give it a custom error message to throw out.
In case this variable or value we pass to the function is falsy such as the value was false, and since
some bool is false, this error message should get printed into the console.
So if we run the game.
We see that the value was false.
Very nice.
Now, just a quick note is that the assert function can be costly if you try to use string concatenation
or string formatting in the second parameter section here.
And that's because string concatenation and formatting is a costly thing to calculate for computers,
especially in Lua, when we try to pass it as a second parameter.
If you need to use string formatting or string concatenation for error messages, then it's probably
better to use an if statement to check whether a value is falsy, and then just use the error function
instead.
For your string concatenation and string formatting needs.
It's actually a lot more performant, and somebody did some testing and found that it's much faster
to use if statements in the error function than it is to use the assert function with string concatenation
and formatting.
So just keep that in mind.
Now moving on we're going to look at the type and type of functions.
So what is the difference between the type and type of functions.
Well the type function is a function that's built into Lua itself that tells you the primitive type
of a value that you pass to the function.
So if you pass a string, it's going to return a string back to you that tells you the name of the type,
such as string, or if it's a number, it's going to return a string back to you that says number and
so on.
So that's the purpose of the type function.
Now the type of function is specific here to Roblox, Lua you.
And that's because it's able to tell you the type of the custom data types in Roblox.
So for example, if you try to pass a vector data type to the type function, it's going to return you
a string that just says user data.
And that's because user data is another primitive type in Lua where people can make their own custom
types, which is what Roblox does with their own custom types like vectors, parts, and so on.
So if you need to specifically check the type of a Roblox data type, that's where you would use the
type of function.
So let's go ahead and demonstrate these functions here real quick.
I'm going to create a vector here.
I'll just call it some vector equal to a vector two dot new.
And we'll just pass zero and zero.
And then I'm also going to create a variable called some string and just set it to high.
And then what we could do is we could print out what the type of some vector is here in the console
using the type function, as well as print out the type of some string.
And then let me comment this out real quick so our script does not error.
So what we should expect to print here is that it should print out user data.
And what we should expect to print out here is string.
So if we run our game.
As you can see, we get user data and we get string.
Now, if we wanted to specifically know whether or not this value was a vector two, then that's where
we would have to use the type of function.
So if I go ahead and print the type of our some vector, it should print vector two.
And there we go.
We get the custom Roblox data type by using the type of function.
And again, this is the only difference between the type and the type of functions.
Now, because we know the type and type of functions, we can use them for type refinement by narrowing
down what the type of a variable is in a specific context, we can avoid errors and make our code easier
to understand.
So if we want to figure out what exactly the type is of the value that gets returned from our get unknown
function, then we can use the type or type of functions.
So let me create a variable here I'm going to call it refinement example.
And we're going to set it to get unknown.
And currently our refinement example is of the type unknown.
So how the heck do we figure out without getting errors in our code or like these red underlines here.
What's the type of refinement?
Example is.
Well we can use the type of function.
So if type of refinement example is equal to the string let's say string.
Then in this context, in this indention here in this block of code right.
This is the context for our script.
We know that refinement example has to be a string.
And the type checking engine is automatically going to infer that for us that's how we're refining the
type here.
We can also do the same.
Else if the type of refinement example is equal to a number.
Then in this particular context we know refinement example is a number, right?
It's that simple.
Now if we were to go and put an else statement here, let's say for some reason refinement example wasn't
a string or a number, which is impossible.
Then this is where refinement example would inhibit the type of never.
Now the never type is considered the bottom type, meaning that there will never be a value in Lua that
has the type of never.
When we are in a context in Lua where we have refined a variable to a point where it has no possible
type, like right here, then it is of the type never.
Now, I don't want you to be so concerned about these three built in types.
You'll likely never use them except for maybe any and unknown.
This lecture is just to expose you to these built in types, and considering many other developers never
use them, I don't expect you to either.
So again, before I end this lecture, I want to be fully and explicitly clear once more.
That type annotation is not required in our code.
It exists simply to help us and other developers understand the types of variables, what types are
required for functions, and what the type is of return values from functions.
We're going to continue learning more about types in the next lectures, so go ahead and try to wrap
up everything in your mind from this lecture.
And I'm going to see you in the next one.